1
Chuyển đổi sang đồ họa hiệu suất cao
AI020Lesson 8
00:00

Trong đồ họa máy tính, chúng ta phân biệt giữa Véc-tơBản bitmap đồ họa. Đồ họa véc-tơ (như SVG) mô tả hình ảnh thông qua các hình dạng logic; mỗi phần tử là một đối tượng tồn tại lâu dài trong DOM. Ngược lại, đồ họa bitmap (như HTML5 Canvas) làm việc với các ma trận điểm màu.

1. Sự chuyển đổi sang Canvas

Mặc dù SVG dễ dàng định kiểu hơn bằng CSS, trình duyệt phải theo dõi từng nút. Đối với các nhu cầu hiệu suất cao, như trò chơi có hàng ngàn thành phần di chuyển, API Canvas vượt trội hơn. Nó cung cấp một phần tử DOM duy nhất bao gồm bề mặt vẽ – gần như một "bảng trắng".

2. Bối cảnh vẽ

Phần tử <canvas> là một "hộp đen" cho đến khi chúng ta khởi tạo bối cảnh. Các phương thức của đối tượng này cung cấp giao diện vẽ thực tế, tách biệt phần tử hiển thị khỏi logic render.

var context = canvas.getContext("2d");

3. Nhận thức về không gian tên

Trong đồ họa dựa trên XML như SVG, thuộc tính xmlns="http://www.w3.org/2000/svg" là rất quan trọng. Nó báo hiệu trình duyệt chuyển từ việc phân tích cú pháp HTML chuẩn sang sơ đồ đồ họa cụ thể, cho phép các thẻ hình dạng được nhận diện như các đối tượng tương tác.

main.py
TERMINALbash — 80x24
> Ready. Click "Run" to execute.
>
", "execution_steps": [ { "output": "Rendering SVG: Cyan circle (updated from red) and blue outlined square." }, { "output": "Rendering Canvas: Solid red rectangle drawn via pixel raster." } ] }; const executionSteps = (courseData && courseData.execution_steps) || []; // ── Machine Translation Data Hydration ── const domCode = document.getElementById('data-course-code'); if (domCode && courseData) { courseData.code = domCode.textContent; } const domExecs = document.querySelectorAll('#data-exec-output .data-exec'); domExecs.forEach(el => { const stepIdx = parseInt(el.getAttribute('data-step'), 10); if (!isNaN(stepIdx) && executionSteps[stepIdx]) { executionSteps[stepIdx].output = el.textContent.trim(); } }); const pageNumber = '1'; let visualMode = 'code'; let studyTimerInterval = null; let studyElapsedTime = 0; let studyLastStartTime = 0; let isStudyTimerRunning = false; let isSimRunning = false; let currentStep = 0; let timer = null; let animFrameId; let startTime = 0; // Global State let masterTimeline = null; let hybridInitialized = false; let totalTimelineSteps = 0; // tracks how many step labels the fallback timeline defines const els = { codeContainer: document.getElementById('code-container'), quizContainer: document.getElementById('quiz-container'), examContainer: document.getElementById('exam-container'), tabCode: document.getElementById('tab-code'), tabSim: document.getElementById('tab-sim'), tabQuiz: document.getElementById('tab-quiz'), tabExam: document.getElementById('tab-exam'), visControls: document.getElementById('vis-controls'), visStatusBar: document.getElementById('vis-status-bar'), code: document.getElementById('code-content'), visContainer: document.querySelector('.vis-container'), tipsBtn: document.getElementById('tips-btn'), drawer: document.getElementById('drawer-overlay'), timerDisplay: document.getElementById('study-timer'), timerVal: document.getElementById('timer-val'), consoleOutput: document.getElementById('console-output'), runBtn: document.getElementById('run-btn') }; // --- HELPERS --- /** * [通用渲染方法] General LaTeX Render Method * 封装 MathJax 渲染逻辑,可被多次调用 * @param {HTMLElement | Array} elements - 可选,指定渲染的元素或元素数组 */ function renderLaTeX(elements) { if (window.MathJax && typeof window.MathJax.typesetPromise === 'function') { // 如果传入了具体元素,只渲染这些元素 if (elements) { //确保 elements 是数组形式 const elArray = Array.isArray(elements) ? elements : [elements]; MathJax.typesetPromise(elArray).catch(err => console.warn('MathJax specific render error:', err)); } else { // 否则渲染全页 MathJax.typesetPromise().catch(err => console.warn('MathJax global render error:', err)); } } } // Syntax Highlighter adapted for Python keywords function renderCode(codeStr) { return codeStr.split('\n').map((line, i) => { const lineNum = i + 1; let htmlLine = ''; const regex = /((?:"(?:[^"\\]|\\.)*"|'(?:[^'\\]|\\.)*'))|(#.*)|(\b(?:def|class|if|else|elif|while|for|in|return|import|from|as|try|except|finally|with|lambda|pass|break|continue)\b)|(\b(?:print|len|range|enumerate|zip|map|filter|set|list|dict|int|str|float|sum|max|min|append|pop)\b)|(\b(?:True|False|None|[0-9]+)\b)|(\+|-|\*|\/|=|<|>|!|%|\[|\]|\{|\}|\(|\))/g; let lastIndex = 0; let match; while ((match = regex.exec(line)) !== null) { const textBefore = line.slice(lastIndex, match.index); htmlLine += escapeHtml(textBefore); const [fullMatch, str, com, kw, fn, num, op] = match; if (str) htmlLine += `${escapeHtml(str)}`; else if (com) htmlLine += `${escapeHtml(com)}`; else if (kw) htmlLine += `${escapeHtml(kw)}`; else if (fn) htmlLine += `${escapeHtml(fn)}`; else if (num) htmlLine += `${escapeHtml(num)}`; else if (op) htmlLine += `${escapeHtml(op)}`; lastIndex = regex.lastIndex; } htmlLine += escapeHtml(line.slice(lastIndex)); return `
${lineNum}
${htmlLine}
`; }).join(''); } function escapeHtml(text) { if (!text) return ''; return text.replace(/&/g, "&").replace(//g, ">").replace(/"/g, """).replace(/'/g, "'"); } // Helper to clear all highlights // Helper to clear all highlights function clearHighlights() { const lines = document.querySelectorAll('.code-line'); lines.forEach(l => l.classList.remove('executing')); } // --- INTERACTION LOGIC --- // NEW: Advanced Simulation with Line Highlighting window.runSimulatedCode = async function () { if (isSimRunning) return; const term = els.consoleOutput; if (!term) return; isSimRunning = true; if (els.runBtn) els.runBtn.disabled = true; try { // Initial Term State term.innerHTML = `
${courseData.run_cmd || '> python3 main.py'}
`; const wait = (ms) => new Promise(r => setTimeout(r, ms)); await wait(400); // In ra tất cả đầu ra cùng lúc (không đánh dấu dòng từng dòng) for (let i = 0; i < executionSteps.length; i++) { const step = executionSteps[i]; if (step.output) { const div = document.createElement('div'); div.className = 'console-line'; div.innerText = step.output; term.appendChild(div); } } // Hoàn thành const cursor = document.createElement('div'); cursor.className = 'console-line'; cursor.innerHTML = `> `; term.appendChild(cursor); term.scrollTop = term.scrollHeight; } catch (err) { console.error("Lỗi mô phỏng", err); } finally { isSimRunning = false; if (els.runBtn) els.runBtn.disabled = false; } }; // Hàm sao chép mã nguồn window.copyCode = function () { if (navigator.clipboard) { navigator.clipboard.writeText(courseData.code).then(() => { const btn = document.querySelector('.ide-btn[onclick="copyCode()"]'); if (btn) { const parent = btn.parentElement; const originalHtml = parent.innerHTML; parent.innerHTML = ` Đã sao chép!`; lucide.createIcons(); setTimeout(() => { parent.innerHTML = originalHtml; lucide.createIcons(); }, 2000); } }); } }; // Chuyển đổi thử thách/Kỳ thi // Logic chọn bài kiểm tra function toggleFullScreen() { const elem = els.visContainer; if (elem) { if (!document.fullscreenElement) { elem.requestFullscreen().catch(err => { console.error(err); }); } else { document.exitFullscreen(); } } } const fsBtn = document.getElementById('fsBtn'); if (fsBtn) { document.addEventListener('fullscreenchange', () => { const isFull = !!document.fullscreenElement; fsBtn.innerHTML = isFull ? '' : ''; if (window.lucide) lucide.createIcons(); }); } // Xử lý câu trả lời bài kiểm tra (kiểu tham chiếu từ InnerPage General) window.checkAnswer = function(card, prefix, isCorrect) { const grid = card.closest('.quiz-options-grid'); if (grid.classList.contains('answered')) return; grid.classList.add('answered'); card.classList.add('selected', isCorrect ? 'correct' : 'incorrect'); const icon = document.createElement('i'); icon.setAttribute('data-lucide', isCorrect ? 'check-circle' : 'x-circle'); icon.style.flexShrink = '0'; card.appendChild(icon); if (window.lucide) lucide.createIcons(); const correctBox = document.getElementById(prefix + '-correct'); const incorrectBox = document.getElementById(prefix + '-incorrect'); if (isCorrect && correctBox) { correctBox.style.display = 'block'; } else if (!isCorrect && incorrectBox) { incorrectBox.style.display = 'block'; } }; // Chuyển đổi lời giải kỳ thi (kiểu tham chiếu từ InnerPage General) window.toggleExamSolution = function(btn) { const ansDiv = btn.nextElementSibling; const isVis = ansDiv.classList.toggle('visible'); btn.innerHTML = isVis ? 'Ẩn lời giải ' : 'Show Solution ', 'Hiện lời giải